package pl.vanta.utils.gui.tables;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.KeyListener;
import java.awt.event.MouseListener;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

import javax.swing.JComponent;
import javax.swing.JScrollPane;
import javax.swing.TransferHandler;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.TableColumn;
import javax.swing.table.TableRowSorter;

public class VTable<E> extends JComponent
{
	private static final long				serialVersionUID	= 1L;

	private VJTable							table;
	private JScrollPane						scrollPane;
	private VTableModel<E>					tableModel;
	private TableRowSorter<VTableModel<E>>	sorter;

	public VTable(VTableModel<E> tableModel)
	{
		this.tableModel = tableModel;

		initComponents();
		setupComponents();
		layoutComponents();
	}

	public void initComponents()
	{
		this.sorter = new TableRowSorter<VTableModel<E>>(this.tableModel);
		this.table = new VJTable(this.tableModel);
		this.scrollPane = new JScrollPane(this.table);
	}

	public void setupComponents()
	{
		this.scrollPane.getViewport().setBackground(Color.WHITE);

		this.table.setSurrendersFocusOnKeystroke(true);
		this.table.setRowSorter(this.sorter);
		this.table.getTableHeader().setReorderingAllowed(false);
		this.table.setColumnSelectionAllowed(false);

		setupRenderers();
		setupColumns();
	}

	private void setupColumns()
	{
		for (int i = 0; i < this.table.getColumnCount(); i++)
		{
			TableColumn column = this.table.getColumnModel().getColumn(i);
			ColumnDescription cd = null;

			if (i == 0 && this.tableModel.isIndex())
			{
				cd = this.tableModel.getIndexColumnDescription();
			}
			else
			{
				cd = this.tableModel.getColumnsDescriptions()[this.tableModel.calculateColumn(i)];
			}

			column.setResizable(cd.isResizable());
			column.setPreferredWidth(cd.getSize());

			this.sorter.setSortable(i, cd.isSortable());

			if (!cd.isResizable())
			{
				column.setMaxWidth(cd.getSize());
				column.setMinWidth(cd.getSize());
			}
		}
	}

	private void setupRenderers()
	{
		for (int i = 0; i < this.table.getColumnCount(); i++)
		{
			if (!(i == 0 && this.tableModel.isIndex()))
			{
				TableColumn column = this.table.getColumnModel().getColumn(i);

				ColumnDescription cd = this.tableModel.getColumnsDescriptions()[this.tableModel.calculateColumn(i)];

				if (cd.getRenderer() != null)
				{
					column.setCellRenderer(cd.getRenderer());
				}
			}
		}
	}

	public void layoutComponents()
	{
		this.setLayout(new BorderLayout());
		this.add(this.scrollPane, BorderLayout.CENTER);
	}

	@Override
	protected void paintComponent(Graphics g)
	{
		int totalWidth = this.table.getSize().width - getIndexColumnSize();

		for (int i = 0; i < this.table.getColumnCount(); i++)
		{
			if (!(i == 0 && this.tableModel.isIndex()))
			{
				ColumnDescription cd = this.tableModel.getColumnsDescriptions()[this.tableModel.calculateColumn(i)];

				if (cd.isResizable())
				{
					TableColumn column = this.table.getColumnModel().getColumn(i);

					int columnWidth = totalWidth * cd.getSize() / 100;

					column.setPreferredWidth(columnWidth);
				}
			}
		}

		super.paintComponent(g);
	}

	private int getIndexColumnSize()
	{
		if (this.tableModel.isIndex())
		{
			return this.tableModel.getIndexColumnDescription().getSize();
		}
		else
		{
			return 0;
		}
	}

	public int removeSelectedItems()
	{
		int[] rows = this.table.getSelectedRows();

		this.tableModel.removeItems(convertRowIndexToModel(this.table.getSelectedRows()));

		return rows.length;
	}

	public E getSelectedItem()
	{
		int index = this.table.getSelectedRow();

		if (index == -1)
		{
			return null;
		}

		return this.tableModel.getItem(convertRowIndexToModel(index));
	}

	public List<E> getSelectedItems()
	{
		return this.tableModel.getItems(convertRowIndexToModel(this.table.getSelectedRows()));
	}

	public Color getBackgroundColor()
	{
		return this.scrollPane.getViewport().getBackground();
	}

	public void setBackgroundColor(Color backgroundColor)
	{
		this.scrollPane.getViewport().setBackground(backgroundColor);
	}

	public void setSelectionMode(VTableSelectionMode selectionMode)
	{
		this.table.setSelectionMode(selectionMode.getValue());
	}

	private int[] convertRowIndexToModel(int[] index)
	{
		int[] tab = new int[index.length];

		for (int i = 0; i < index.length; i++)
		{
			tab[i] = convertRowIndexToModel(index[i]);
		}

		return tab;
	}

	private int convertRowIndexToModel(int index)
	{
		if (index < 0 || index >= this.tableModel.getRowCount())
		{
			return -1;
		}

		return this.table.convertRowIndexToModel(index);
	}

	public void removeAllItems()
	{
		this.tableModel.removeAllItems();
	}

	public <T extends E> void addItem(T item)
	{
		this.tableModel.addItem(item);
	}

	public void addItems(Collection<? extends E> items)
	{
		this.tableModel.addItems(items);
	}

	public void addSelectionListener(ListSelectionListener listener)
	{
		this.table.getSelectionModel().addListSelectionListener(listener);
	}

	public void removeSelectionListener(ListSelectionListener listener)
	{
		this.table.getSelectionModel().removeListSelectionListener(listener);
	}

	public List<E> getAllItems()
	{
		return Collections.unmodifiableList(this.tableModel.getAllItems());
	}

	public synchronized void addKeyListener(KeyListener l)
	{
		this.table.addKeyListener(l);
	}

	public synchronized void removeKeyListener(KeyListener l)
	{
		this.table.removeKeyListener(l);
	}

	public synchronized void addMouseListener(MouseListener l)
	{
		this.table.addMouseListener(l);
	}

	public synchronized void removeMouseListener(MouseListener l)
	{
		this.table.removeMouseListener(l);
	}

	public void clearSelection()
	{
		this.table.getSelectionModel().clearSelection();
	}

	public void refresh()
	{
		int[] rows = this.table.getSelectedRows();
		this.tableModel.fireTableDataChanged();
		setSelectedRows(rows);
	}

	public void setSelectedRows(int[] rows)
	{
		for (int i = 0; i < rows.length; i++)
		{
			this.table.setRowSelectionInterval(rows[i], rows[i]);
		}
	}

	private int convertRowIndexToView(int index)
	{
		return this.table.convertRowIndexToView(index);
	}

	// private int[] convertRowIndexToView(int[] index)
	// {
	// int[] tab = new int[index.length];
	//
	// for (int i = 0; i < index.length; i++)
	// {
	// tab[i] = convertRowIndexToView(index[i]);
	// }
	//
	// return tab;
	// }

	public synchronized KeyListener[] getKeyListeners()
	{
		return this.table.getKeyListeners();
	}

	public int[] getSelectedRows()
	{
		return this.table.getSelectedRows();
	}

	public int getSelectedRow()
	{
		return this.table.getSelectedRow();
	}

	public E getItem(int rowIndex)
	{
		if (rowIndex < 0 || rowIndex >= this.tableModel.getRowCount())
		{
			return null;
		}

		return this.tableModel.getItem(convertRowIndexToModel(rowIndex));
	}

	public int getItemIndex(E item)
	{
		if (item == null)
		{
			return -1;
		}

		for (int i = 0; i < this.tableModel.getRowCount(); i++)
		{
			if (item.equals(this.tableModel.getItem(i)))
			{
				return convertRowIndexToView(i);
			}
		}

		return -1;
	}

	public void colorItem(Color color, E item)
	{
		int index = this.getItemIndex(item);

		colorRow(color, index);
	}

	public void colorRow(Color color, int row)
	{
		this.table.setColorForRow(color, row, true);
	}

	public void colorRows(Color color, int[] rows)
	{
		for (int i : rows)
		{
			this.table.setColorForRow(color, rows[i], false);
		}

		this.table.repaint();
	}

	public void clearColoredRows()
	{
		this.table.clearColoredRows();
	}

	public void clearRowColor(int row)
	{
		this.table.clearRowColor(row);
	}

	public void clearItemColor(E item)
	{
		int index = getItemIndex(item);

		clearRowColor(index);
	}

	public int getRowsCount()
	{
		return this.table.getRowCount();
	}

	public void setTransferHandler(TransferHandler newHandler)
	{
		this.table.setDragEnabled(true);
		this.table.setTransferHandler(newHandler);
	}

	public void sort(Comparator<E> comparator)
	{
		this.tableModel.sort(comparator);
	}
}